<?php

class NpcOrderService extends AbstractService
{
    /**
     * @var NpcOrderService
     */
    private static $instance;

    /**
     * @return NpcOrderService
     */
    public static function get() {
        if (!self::$instance) {
            self::$instance = new NpcOrderService();
        }
        return self::$instance;
    }

    /**
     * Construction.
     */
    protected function __construct() {
        parent::__construct();
    }

    /**
     * 处理创建时的逻辑
     * @param int $userId
     */
    public function handleCreate($userId) {

    }

    /**
     * 处理登录时的逻辑
     * @param int $userId
     */
    public function handleLogin($userId) {

    }

    /**
     * 提供当前 service 数据
     */
    public function info($userId) {
        return $this->getNpcOrder($userId, GameService::get()->getReqTime());
    }

    /**
     * @param $userId
     * @return array
     * @throws SzException
     */
    public function getNpcOrder($userId, $reqTime) {
        $sysConfig = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GAME_SYSTEM_UNLOCK, GameConst::FUNC_NPC_ORDER, true);
        $userLevel = UserService::get()->getUserLevel($userId);
        if ($sysConfig['level'] > $userLevel) {
            return array();
        }
        $profileInfoList = SzPersister::get()->getVoList($userId, ModuleProfileConst::ORM_NAME_PROFILE_INFO);
        $npcOrderList = SzPersister::get()->getVoList($userId,GameConst::ORM_NAME_NPC_ORDER);

        foreach ($npcOrderList->toArray() as $k=>$v) {
            if ($k > 3) {
                $npcOrderList->deleteElement($k,false);
            }
        }

        /**
         * @var ModuleProfileInfoVo $npcOrderRefreshTimeInfo
         */
        $npcOrderRefreshTimeInfo = $profileInfoList->getElement(GameConst::USER_INFO_TYPE_NPC_ORDER_REFRESH_TIME, false);
        $npcOrderOldList = $npcOrderList->toArray();
        $npcOrderNewList = array();

        $npcOrderLevelSegmentSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_LEVEL_SEGMENT, $userLevel, true);
        if (!$npcOrderLevelSegmentSetting) {
            return array();
        }

        $npcOrderWalkSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_WALK, null, true);
        if (!$npcOrderWalkSettings) {
            return array();
        }

        $npcOrderJudgmentSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_JUDGMENT, null, true);
        if ($npcOrderJudgmentSetting) {
            foreach ($npcOrderJudgmentSetting as $vaseId=>$demandLibrary) {
                /**
                 *  @var ModuleItemVoList $itemList
                 */
                $itemList = SzPersister::get()->getVoList($userId, ModuleItemConst::ORM_NAME_ITEM);
                $item = $itemList->getElementsByItemDefId($vaseId);
                if (is_null($item)) {
                    foreach ($demandLibrary['Id'] as $id) {
                        foreach ($npcOrderLevelSegmentSetting['demandLibrary'] as $k=>$v) {
                            if ($v == $id) {
                                unset($npcOrderLevelSegmentSetting['demandLibrary'][$k]);
                            }
                        }
                    }
                }
            }
        }

        if (!$npcOrderRefreshTimeInfo) {
            $npcOrderRefreshTimeInfo = new ModuleProfileInfoVo(
                $userId,
                GameConst::USER_INFO_TYPE_NPC_ORDER_REFRESH_TIME,
                $reqTime,
                $reqTime,
                true
            );
            $profileInfoList->addElement($npcOrderRefreshTimeInfo);
            $profileInfoList->setElement(GameConst::USER_INFO_TYPE_NPC_ORDER_REFRESH_TIME, $npcOrderRefreshTimeInfo);

            $key = array_rand($npcOrderWalkSettings);
            $npcOrderWalkSetting = $npcOrderWalkSettings[$key];
            $indexId = $npcOrderWalkSetting['indexId'];

            $taskNpc = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'taskNpc', true);
            $taskNpc = json_decode($taskNpc['value'],true);
            $key = array_rand($taskNpc);
            $npc = $taskNpc[$key];

            $key = array_rand($npcOrderLevelSegmentSetting['demandLibrary']);
            $taskId = $npcOrderLevelSegmentSetting['demandLibrary'][$key];

            $npcOrderTaskConditionSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_TASK_CONDITION, $taskId, true);
            if (!$npcOrderTaskConditionSetting) {
                throw new SzException(80001,GameConst::CONFIG_NPC_ORDER_TASK_CONDITION.'-'.$taskId);
            }
            list($min,$max) = $npcOrderTaskConditionSetting['ratio'];
            $ratio = mt_rand($min,$max);

            $npcOrder = new NpcOrderVo($userId,$indexId,$taskId,$npc,$ratio,0,true);
            $npcOrderList->addElement($npcOrder);
            $npcOrderList->setElement($indexId,$npcOrder);
            SzPersister::get()->setVoList($npcOrderList);
            SzPersister::get()->setVoList($profileInfoList);
            $npcOrderRefreshTime = $reqTime;
            $npcOrderNewList[] = $npcOrder->toArray();
        } else {
            $npcOrderRefreshTime = (int)$npcOrderRefreshTimeInfo->getRawInfo();
            $npcRefreshTime = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'npcRefreshTime', true);
            $mostTasksExist = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'mostTasksExist', true);
            if ( ($reqTime - $npcOrderRefreshTime) > $npcRefreshTime['value'] && $mostTasksExist['value'] > count($npcOrderList->getList())) {
                $num1 = $mostTasksExist['value'] - count($npcOrderList->getList());
                $num2 = (int)(($reqTime - $npcOrderRefreshTime) / $npcRefreshTime['value']);
                $num = min($num1,$num2);

                $taskNpc = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'taskNpc', true);
                $taskNpc = json_decode($taskNpc['value'],true);
                $npcList = array();
                if ($npcOrderList->getList()) {
                    foreach ($npcOrderList->toArray() as $val) {
                        $npcList[] = $val['npc'];
                        unset($npcOrderWalkSettings[$val['indexId']]);
                    }
                }

                for ($i=0; $i<$num; $i++) {
                    $taskNpc = array_diff($taskNpc,$npcList);
                    $key = array_rand($taskNpc);
                    $npc = $taskNpc[$key];
                    $npcList[] = $npc;
                    $key = array_rand($npcOrderWalkSettings);
                    $npcOrderWalkSetting = $npcOrderWalkSettings[$key];
                    $indexId = $npcOrderWalkSetting['indexId'];
                    unset($npcOrderWalkSettings[$indexId]);
                    $key = array_rand($npcOrderLevelSegmentSetting['demandLibrary']);
                    $taskId = $npcOrderLevelSegmentSetting['demandLibrary'][$key];

                    $npcOrderTaskConditionSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_TASK_CONDITION, $taskId, true);
                    if (!$npcOrderTaskConditionSetting) {
                        throw new SzException(80001,GameConst::CONFIG_NPC_ORDER_TASK_CONDITION.'-'.$taskId);
                    }
                    list($min,$max) = $npcOrderTaskConditionSetting['ratio'];
                    $ratio = mt_rand($min,$max);
                    $npcOrder = new NpcOrderVo($userId,$indexId,$taskId,$npc,$ratio,0,true);

                    $npcOrderList->addElement($npcOrder);
                    $npcOrderList->setElement($indexId,$npcOrder);
                    $npcOrderNewList[] = $npcOrder->toArray();
                }
                $npcOrderRefreshTimeInfo->setInfo($reqTime);
                $profileInfoList->updateElement(GameConst::USER_INFO_TYPE_NPC_ORDER_REFRESH_TIME,$npcOrderRefreshTimeInfo);
                SzPersister::get()->setVoList($npcOrderList);
                SzPersister::get()->setVoList($profileInfoList);
                $npcOrderRefreshTime = $reqTime;
            }
        }
        return array(
            'npcOrderOldList' => $npcOrderOldList,
            'npcOrderNewList' => $npcOrderNewList,
            'npcOrderRefreshTime' => $npcOrderRefreshTime,
        );

    }

    /**
     * @param $userId
     * @param $indexId
     * @param $status
     * @param $reqTime
     * @return array
     * @throws SzException
     */
    public function complete($userId,$indexId,$status,$reqTime) {
        /**
         * @var NpcOrderVo $npcOrder
         */
        $npcOrderList = SzPersister::get()->getVoList($userId,GameConst::ORM_NAME_NPC_ORDER);
        $randomDrop = 0;

        // 网络请求延迟时，第二次点击实际上订单已完成，这里是找不到的，直接返回
        try {
            $npcOrder = $npcOrderList->getElement($indexId);
        } catch (\Exception $exception) {
            if (!$npcOrder) {
                return array(
                    'reqTime' => $reqTime,
                    'randomDrop' => $randomDrop
                );
            }
        }

        $taskId = $npcOrder->getOrderId();
        $ratio = $npcOrder->getRatio() ? $npcOrder->getRatio() : 1;

        $mostTasksExist = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'mostTasksExist', true);
        $userNum = count($npcOrderList->getList());
        if ($status) {
            // 提交订单
            $npcOrderTaskConditionSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_TASK_CONDITION, $taskId, true);
            if (!$npcOrderTaskConditionSetting) {
                throw new SzException(80001,GameConst::CONFIG_NPC_ORDER_TASK_CONDITION.'-'.$taskId);
            }

            list($itemList) = ItemService::get()->processItems($userId, [$npcOrderTaskConditionSetting['demandItem'] => -($npcOrderTaskConditionSetting['quantity'] * $ratio)]);
            $input = $npcOrderTaskConditionSetting['demandItem'] .':' .($npcOrderTaskConditionSetting['quantity'] * $ratio) . '|';
            $output = '';
            SzPersister::get()->setVoList($itemList);

            $addition = 1;
            $orderBuffStatus = VideoService::get()->getBuffStatus($userId,GameConst::VIDEO_BUFF_NPC_ORDER,$reqTime);
            if ($orderBuffStatus) {
                $videoSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_VIDEO, GameConst::VIDEO_BUFF_NPC_ORDER, true);
                if ($videoSetting) {
                    $addition = $videoSetting['sp_reward'][0];
                }
            }
            $num = mt_rand(1,100);

            if ($num <= $npcOrderTaskConditionSetting['probability']) {
                foreach ($npcOrderTaskConditionSetting['token'] as $entryId=>$count) {
                    $count = $count * $ratio;
                    GameService::get()->handleEntity($userId, $entryId, $count, true, true);
                    $output .= $entryId.':'.$count.'|';
                }
                $randomDrop = 1;
            }

            $reward = array();
            $ikebanaFlowerConfig = SzConfig::get()->loadAppConfig(GameConst::CONFIG_IKEBANA_ARRANGEMENT_TWO, $npcOrderTaskConditionSetting['Id'], true)['flowerCombinationId'];//$npcOrderTaskConditionSetting['demandItem']
            foreach ($ikebanaFlowerConfig as $flower=>$flowerNum){
                if (!isset($reward[GameConst::ENTITY_ACTION_GET_TOKEN])) {
                    $reward[GameConst::ENTITY_ACTION_GET_TOKEN] = 0;
                }

                if (!isset($reward[GameConst::ENTITY_ACTION_GET_EXP])) {
                    $reward[GameConst::ENTITY_ACTION_GET_EXP] = 0;
                }

                $flowerReward = SzConfig::get()->loadAppConfig(GameConst::CONFIG_ORDER_REWARD, $flower, true);
                $goldNum = $flowerReward['gold']*$flowerNum;
                $experienceNum = $flowerReward['experience']*$flowerNum;
                //20200409
                //获取玩家该花朵是否具有金币数量加成
                $cashBuff = FlowersUpgradeService::get()->getFlowerBuff($userId,$flower,GameConst::FLOWER_TYPE_1);
                if (!empty($cashBuff)){
                    if ($cashBuff[0] == 2){
                        $goldNum = ($flowerReward['gold'] + $cashBuff[1]) * $flowerNum;
                    }
                    if ($cashBuff[0] == 1){
                        $goldNum = ceil($flowerReward['gold']*(1+$cashBuff[1]/100)) * $flowerNum;
                    }
                }

                //20200409
                //获取玩家该花朵是否具有经验加成
                $experienceBuff = FlowersUpgradeService::get()->getFlowerBuff($userId,$flower,GameConst::FLOWER_TYPE_2);
                if (!empty($experienceBuff)){
                    if ($experienceBuff[0] == 2){
                        $experienceNum = ($flowerReward['experience'] + $experienceBuff[1]) * $flowerNum;
                    }
                    if ($experienceBuff[0] == 1){
                        $experienceNum = ceil($flowerReward['experience']*(1+$experienceBuff[1]/100)) * $flowerNum;
                    }
                }
                $reward[GameConst::ENTITY_ACTION_GET_TOKEN] += intval($goldNum);
                $reward[GameConst::ENTITY_ACTION_GET_EXP] += intval($experienceNum);
            }

            $goldConstBuff = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'npcGoldAddition', true)['value'];
            $expConstBuff = SzConfig::get()->loadAppConfig(ModuleProfileConst::CONFIG_PROFILE, 'npcExpAddition', true)['value'];
            foreach ($reward as $entryId=>$data){
                $data = $data * $ratio;
                if ($orderBuffStatus && $entryId == GameConst::ENTITY_ACTION_GET_TOKEN) {
                    $data = $data * $addition;
                }
                if ($goldConstBuff && $entryId == GameConst::ENTITY_ACTION_GET_TOKEN) {
                    $data = ceil($data * ($goldConstBuff/100));
                }

                if ($expConstBuff && $entryId == GameConst::ENTITY_ACTION_GET_EXP){
                    $data = ceil($data * ($expConstBuff/100));
                }

                GameService::get()->handleEntity($userId, $entryId, $data, true, true);
                $output .= $entryId.':'.$data.'|';
            }
            CommonFunctionService::get()->reportResourceUseDetails(GameConst::FUNC_NPC_ORDER,$userId,1,$output,'complete');//获得资源上报
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
                'clientData' => array(
                    'tid' => 'Module:task|action:npc|',
                    'item' => $taskId,
                    'value' => 1
                )
            ));

            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
                'clientData' => array(
                    'tid' => $output,
                    'item' => 'task_npc&'.$taskId,
                )
            ));

            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_INPUT, array(
                'clientData' => array(
                    'tid' => $input,
                    'item' => 'task_npc&'.$taskId,
                )
            ));

        } else {
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
                'clientData' => array(
                    'tid' => 'Module:task|action:reject|',
                    'item' => $taskId,
                    'value' => 1
                )
            ));
        }
        if ($mostTasksExist == $userNum) {
            UserService::get()->updateProfileInfo($userId, GameConst::USER_INFO_TYPE_NPC_ORDER_REFRESH_TIME, $reqTime);
        }
        $npcOrderList->deleteElement($indexId,$npcOrder);
        SzPersister::get()->setVoList($npcOrderList);
        if ($status) {
            NewTaskService::get()->updateProgress($userId,GameConst::TASK_COMPLETE_ORDER,1);
            TreeService::get()->updateProgress($userId,GameConst::TREE_TASK_COMPLETE_NPC_ORDER,1);
            NewTaskService::get()->updateDailyTask($userId,GameConst::DAILY_TASK_NPC_ORDER,1);
            TrainingService::get()->updateProgress($userId,GameConst::TRAINING_TASK_NPC_ORDER,1);

            // 更新活动任务进度
            $activityData = ActivityService::get()->getActivityIdAndPosition($userId,GameConst::ACTIVITY_TASK_5);
            if (!empty($activityData)){
                ActivityTaskService::get()->updateActivityTaskProgress($userId,$activityData['aid'],$activityData['position']);
            }

            // 更新合约任务进度
            $contractData = ContractService::get()->getContractOpenAndTask($userId,GameConst::CONTRACT_TASK_2,$reqTime);
            if (!empty($contractData) && !empty($contractData['task']) && !empty($contractData['session'])){
                if ($reqTime >= $contractData['start'] && $reqTime <= $contractData['end']){
                    ContractService::get()->updateTaskProgress($userId,$contractData['session'],$contractData['task']['pos'],$contractData['task']['taskId'],$reqTime);
                }
            }

            //社团竞赛任务进度添加
            UnionCompetitionService::get()->addCompetitionTaskProcess($userId,GameConst::UNION_COMPETITION_TASK_5, $reqTime,1);
        }

        return array(
            'reqTime' => $reqTime,
            'randomDrop' => $randomDrop
        );
    }

    /**
     * 跳过订单
     * @param $userId
     * @param $indexId
     * @return array
     * @throws SzException
     */
    public function jumpOrder($userId,$indexId) {
        $sysConfig = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GAME_SYSTEM_UNLOCK, GameConst::FUNC_NPC_ORDER, true);
        $userLevel = UserService::get()->getUserLevel($userId);
        if ($sysConfig['level'] > $userLevel) {
            return array();
        }
        /**
         * @var NpcOrderVo $npcOrder
         */
        $npcOrderList = SzPersister::get()->getVoList($userId,GameConst::ORM_NAME_NPC_ORDER);
        $npcOrder = $npcOrderList->getElement($indexId);
        $isJump = $npcOrder->getIsJump();

        if ($isJump){
            throw new SzException(90081);
        }
        $npcOrderLevelSegmentSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_LEVEL_SEGMENT, $userLevel, true);
        $npcOrderJudgmentSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_JUDGMENT, null, true);
        if ($npcOrderJudgmentSetting) {
            foreach ($npcOrderJudgmentSetting as $vaseId=>$demandLibrary) {
                /**
                 *  @var ModuleItemVoList $itemList
                 */
                $itemList = SzPersister::get()->getVoList($userId, ModuleItemConst::ORM_NAME_ITEM);
                $item = $itemList->getElementsByItemDefId($vaseId);
                if (is_null($item)) {
                    foreach ($demandLibrary['Id'] as $id) {
                        foreach ($npcOrderLevelSegmentSetting['demandLibrary'] as $k=>$v) {
                            if ($v == $id) {
                                unset($npcOrderLevelSegmentSetting['demandLibrary'][$k]);
                            }
                        }
                    }
                }
            }
        }

        //刷新订单任务数据
        if (!$npcOrderLevelSegmentSetting) {
            throw new SzException(80001,GameConst::CONFIG_NPC_ORDER_LEVEL_SEGMENT);
        }
        $key = array_rand($npcOrderLevelSegmentSetting['demandLibrary']);
        $newOrderId = $npcOrderLevelSegmentSetting['demandLibrary'][$key];

        $npcOrderTaskConditionSetting= SzConfig::get()->loadAppConfig(GameConst::CONFIG_NPC_ORDER_TASK_CONDITION, $newOrderId, true);
        if (!$npcOrderTaskConditionSetting) {
            throw new SzException(80001,GameConst::CONFIG_NPC_ORDER_TASK_CONDITION.'-'.$newOrderId);
        }
        list($min,$max) = $npcOrderTaskConditionSetting['ratio'];
        $ratio = mt_rand($min,$max);

        //更新数据
        $npcOrder->setOrderId($newOrderId);
        $npcOrder->setRatio($ratio);
        $npcOrder->setIsJump(1);

        $npcOrderList->updateElement($indexId,$npcOrder);
        SzPersister::get()->setVoList($npcOrderList);

        return array(
            'orderId' => $newOrderId,
            'ratio' => $ratio,
            'isJump' => 1
        );
    }
}
